package ar.com.equis.flexjavaintegration.dao;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import ar.com.equis.flexjavaintegration.excepciones.NegocioExcepcion;

/**
 * Implementacion Generica para <i><b>Hibernate</b></i> y <i><b>Spring</b></i>, superclase de todo Data Access Object.<br/>
 * Contiene implementaciones de metodos de operaciones <b>CRUD</b> basicas.
 * @author MG Systems
 *
 * @param <T>
 */
@SuppressWarnings("unchecked")
public abstract class GenericDAOHibernateImplementation<T> extends HibernateDaoSupport implements IGenericDAO<T> {

	protected Class<?> representedClass;

    public abstract void setRepresentedClass(Class<?> representedClass);
    
    public GenericDAOHibernateImplementation(SessionFactory sf){
    	this.setSessionFactory(sf);
    }
    /**
     * Crea una nueva entidad.
     * @param entity
     * @return representacion del id generado
     */
    public Serializable save(T entity) {
       return getHibernateTemplate().save(entity);
    }

    /**
     * Elimina la entidad.
     * @param entity
     */
    public void delete(T entity) {
        getHibernateTemplate().delete(entity);
    }

    /**
     * Retorna todas las instancias de la Entidad representada.
     * @return List con 0 o mas elementos
     */
    public List<T> findAll() {
    	return (List<T>) getHibernateTemplate().loadAll(representedClass);
    }

    /**
     * Crea o Actualiza la entidad, dependiendo si tiene o no un id seteado existente.
     * @param entity
     */
    public void saveOrUpdate(T entity) {
        getHibernateTemplate().saveOrUpdate(entity);
    }

    /**
     * Crea una nueva instancia.
     * @param entity
     */
    public void persist(T entity){
       getHibernateTemplate().persist(entity);
    }

    /**
     * Retorna una instancia de la Entidad representada por el parametro id<br/>
     * @param id
     * @return
     * @throws DataAccessException: si no existe la instancia para el id indicado
     */
    public T load(Serializable id){
       return (T) getHibernateTemplate().load(representedClass, id);
    }

    /**
     * Actualiza. y si el nombre lo dice todo
     * @param entity
     */
    public void update(T entity) {
    	getHibernateTemplate().update(entity);
    }

    /**
     * Crea una Instancia de una Implmentacion de la interfaz<i>org.hibernate.Criteria</i> para la clase representada.
     * @return Criteria
     */
    public Criteria createCriteria(){
    	return getSessionFactory().getCurrentSession().createCriteria(representedClass);
    }
    
    /**
     * Ejecuta la busqueda por el "criterio" indicado.
     * @param criterion
     * @return List con 0 o mas elementos.
     */
    public List<T> findByCriteria(Criterion criterion){
    	DetachedCriteria criteria = DetachedCriteria.forClass(representedClass).add(criterion);
    	return getHibernateTemplate().findByCriteria(criteria);
    }
   
    /**
     * Ejecuta la busqueda por el "criterio" indicado<br/>
     * retorna un elemento o null si no existe quien cumpla con el criterio
     * @param criterion
     * @return
     * @throws NegocioExcepcion: si existe mas de un elemento para el criterio
     */
    public T findUniqueByCriteria(Criterion criterion) throws NegocioExcepcion{
    	List<T> results = findByCriteria(criterion);
    	if(results.size() > 1){
    		throw new NegocioExcepcion("No unique");
    	}
    	
    	return results.isEmpty()? null: results.get(0); 
    }

}
